﻿// Server Message

using System;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Text;
using System.Collections;
using System.Threading;

namespace chat_client_sp
{
    /// <summary>
    /// SMessage is a LTV message, the length include legth of T and V.
    /// i.e. 
    /// 0x00000006  - length = len(type) + len(value)
    /// 0x00000001  - type (4byte)
    /// 0xffff      - value (2byte
    /// </summary>
    public  class BaseMessage
    {
        public int Length;
        public int MessageType;
        public byte[] Value = null;

        /// <summary>
        /// null constructor
        /// </summary>
        public BaseMessage()
        {
        }

        /// <summary>
        /// default constructor, default type is BASIC
        /// </summary>
        /// <param name="_value"></param>
        public BaseMessage(byte[] _value)
        {
            MessageType = (int)MessageTypeEnum.BASIC;
            Value = new byte[_value.Length];
            _value.CopyTo(Value, 0);
            Length = Value.Length + 4;
        }

        /// <summary>
        /// construct from type and valie fields
        /// </summary>
        /// <param name="_msgType"></param>
        /// <param name="_value"></param>
        public BaseMessage(int _msgType, byte[] _value)
        {
            MessageType = _msgType;
            if (_value.Length > 0)
            {
                Value = new byte[_value.Length];
                _value.CopyTo(Value, 0);
                Length = Value.Length + 4;
            }
            
        }

        /// <summary>
        /// Parse raw packet buffer into SFMessage 
        /// </summary>
        /// <param name="_headerBuf"></param>
        public static  BaseMessage Parse(byte[] _headerBuf)
        {
            if (_headerBuf == null)
                return null;

            BaseMessage msg = new BaseMessage();

            byte[] len_buf = new byte[4];
            byte[] type_buf = new byte[4];

            Buffer.BlockCopy(_headerBuf, 0, len_buf, 0, 4);
            msg.Length = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(len_buf, 0));
            
            // validate the packet 
            if (msg.Length != _headerBuf.Length - 4)
            {
                msg = null;
                throw new ArgumentException("invalid packet! length is not valid!");
            }

            Buffer.BlockCopy(_headerBuf, 4, type_buf, 0, 4);
            msg.MessageType = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(type_buf, 0));
            if (msg.Length - 4 > 0)
            {
                msg.Value = new byte[msg.Length - 4];
                Buffer.BlockCopy(_headerBuf, 8, msg.Value, 0, msg.Value.Length);
            }

            return msg;
        }

        /// <summary>
        /// get the whold binary representation of Message
        /// including LTV 
        /// </summary>
        /// <returns></returns>
        public virtual byte[] GetBytes()
        {
            byte[] buff = new byte[Length + 4];
            int offset = 0;

            Buffer.BlockCopy(BitConverter.GetBytes(IPAddress.HostToNetworkOrder(Length)), 0, buff, offset, 4);
            offset += 4;
            
            Buffer.BlockCopy(BitConverter.GetBytes(IPAddress.HostToNetworkOrder(MessageType)), 0, buff, offset, 4);
            offset += 4;

            if (Length - 4 > 0)
            {
                Buffer.BlockCopy(Value, 0, buff, offset, Length - 4);
            }

            return buff;
        }

        public virtual void Dump()
        {
            Console.WriteLine("[{0,3}] - {1} : Dumping Message : ",
                    Thread.CurrentThread.ManagedThreadId,
                    DateTime.Now.ToString("HH:mm:ss.ffff"));
            Console.WriteLine("    Length: {0}", Length);
            Console.WriteLine("    Type  : {0}", Enum.GetName(typeof(MessageTypeEnum), MessageType));
            Console.Write("    Value: ");
            if (Value !=null)
            {
                foreach (byte b in Value)
                {
                    Console.Write("{0:x}, ", b);
                }
            }
            Console.WriteLine();
        }

        public virtual void DumpHex()
        {
            Console.WriteLine("HexDump");
            foreach (byte b in GetBytes())
            {
                Console.Write("{0,2:X}, ", b);
            }
            Console.WriteLine();
        }
    }


    public class SMessageUnitTest
    {
        public static void TestConstrcutFromFields()
        {
            byte[] val_buf = new byte[4] { 0xff, 0xff, 0xff, 0xff};
            BaseMessage sm = new BaseMessage(0x00, val_buf);
            sm.Dump();
            sm.DumpHex();
        }



        public static void TestConstructFromBytes()
        {
            byte[] val_buf = new byte[] {
                0x00, 0x00, 0x00, 0x06,     // length
                0x00, 0x00, 0x00, 0x00,     // msg type 
                0xff, 0xff};                // value
            BaseMessage sm = BaseMessage.Parse(val_buf);
            sm.Dump();
            sm.DumpHex();
        
        }

        // following construction should fail due to not valid
        // will raise an exception
        public static void TestConstructFromBytesFail()
        {
            byte[] val_buf = new byte[] { 
                0x00, 0x00, 0x00, 0x06,
                0x00, 0x00, 0x00, 0x02,
                0xff, 0xff, 0xff, 0xff};
            BaseMessage sm = BaseMessage.Parse(val_buf);
            sm.Dump();
        }
    }

}